package ast;

import java.io.PrintStream;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;

public class LogicalExpr extends CondExpr {
	public static final int AND = 1;
	public static final int OR = 2;
	public static final int NOT = 3;
	public final CondExpr expr1, expr2;
	public final int op;

	public LogicalExpr(CondExpr e1, int oper, CondExpr e2, Location loc) {
		super(loc);
		expr1 = e1;
		expr2 = e2;
		op = oper;
	}

	public LogicalExpr(CondExpr e1, int oper, Location loc) {
		this(e1, oper, null, loc); // used for NOT
	}

	public void print(PrintStream ps) {
		if (op == NOT) {
			ps.print("!(");
			expr1.print(ps);
			ps.print(")");
			return;
		}
		ps.print("(");
		expr1.print(ps);
		switch (op) {
			case AND:
				ps.print("&&");
				break;
			case OR:
				ps.print("||");
				break;
		}
		expr2.print(ps);
		ps.print(")");
	}

	// interpret: evaluate the logical expression (AND, OR, NOT), return 1 if true, 0 if false
	public AbstractBoolValue evaluate(Stack<Map<String, AbstractValue>> stateStack, Scanner scanner) throws Exception {
		if (op == NOT) {
			AbstractBoolValue val = expr1.evaluate(stateStack, scanner);
			if (val == AbstractBoolValue.True) {
				return AbstractBoolValue.False;
			} else if (val == AbstractBoolValue.False) {
				return AbstractBoolValue.True;
			} else {
				return AbstractBoolValue.AnyBool;
			}
		}

		switch (op) {
			case AND:
				/**
				 * True && True = True
				 * True && False = False
				 * True && AnyBool = AnyBool
				 *
				 * False && True = False
				 * False && False = False
				 * False && AnyBool = False
				 *
				 * AnyBool && True = AnyBool
				 * AnyBool && False = False
				 * AnyBool && AnyBool = AnyBool
				 */
				// short circuit
				if (expr1.evaluate(stateStack, scanner) == AbstractBoolValue.False) {
					return AbstractBoolValue.False;
				}
				if (expr2.evaluate(stateStack, scanner) == AbstractBoolValue.False) {
					return AbstractBoolValue.False;
				}
				if (expr1.evaluate(stateStack, scanner) == AbstractBoolValue.True && expr2.evaluate(stateStack, scanner) == AbstractBoolValue.True) {
					return AbstractBoolValue.True;
				}
				return AbstractBoolValue.AnyBool;
			case OR:
				/**
				 * True || True = True
				 * True || False = True
				 * True || AnyBool = True
				 *
				 * False || True = True
				 * False || False = False
				 * False || AnyBool = AnyBool
				 *
				 * AnyBool || True = True
				 * AnyBool || False = AnyBool
				 * AnyBool || AnyBool = AnyBool
				 */
				// short circuit
				if (expr1.evaluate(stateStack, scanner) == AbstractBoolValue.True) {
					return AbstractBoolValue.True;
				}
				if (expr2.evaluate(stateStack, scanner) == AbstractBoolValue.True) {
					return AbstractBoolValue.True;
				}
				if (expr1.evaluate(stateStack, scanner) == AbstractBoolValue.False && expr2.evaluate(stateStack, scanner) == AbstractBoolValue.False) {
					return AbstractBoolValue.False;
				}
				return AbstractBoolValue.AnyBool;
		}
		return null;
	}
}
